Ownership

** Ownership ** is a set of rules that govern how a Rust program manages memory.

Memory is managed through a system of ownership with a set of rules that the compiler checks

Stack and Heap

Ownership Rules

Variable Scope

A scope is the range within a program of which an item is valid.

{ 
			// s is not valid here, since it's not yet declared 
	let s = "hello"; // s is valid from this point forward 
	
	// do stuff with s 
} // this scope is now over, and s is no longer valid
let mut s = String::from("hello");
s.push_str(", world!"); // push_str() appends a literal to a String
println!("{s}"); // this will print `hello, world!`

Memory and Allocation

let s1 = String::from("hello");
let s2 = s1;   // s1 was moved to s2; s1 is no longer valid and pointing to a data on heap that needs to be "dropped"

Rust will never automatically create "deep" copies of your data

let mut s = String::from("hello");
s = String::from("ahoy");

println!("{s}, world!"); // print out "ahoy, world!"

References and Borrowing

A reference is like a pointer in that it's an address we can follow to access the data stored at that address; that data is owned by some other variable.

Unlike a pointer, a reference is guaranteed to point to a valid value of a particular type for the life of that reference.

fn main() { 
	let s1 = String::from("hello"); 
	let len = calculate_length(&s1); 
	
	println!("The length of '{s1}' is {len}."); 
} 

fn calculate_length(s: &String) -> usize { s.len() }

we call the action of creating a reference borrowing.

A variable by default is immutable; so is a reference

A mutable reference has one BIG restriction: if there is a mutable reference to a value, you can have NO OTHER references to that value.

Data race

Data races cause undefined behavior and can be difficult to diagnose and fix.

We also cannot have a mutable reference while we have an immutable one to the same value

    let mut s = String::from("hello");

    let r1 = &s; // no problem
    let r2 = &s; // no problem
    println!("{r1} and {r2}");
    // Variables r1 and r2 will not be used after this point.

    let r3 = &mut s; // no problem
    println!("{r3}");

The scopes of the immutable references r1 and r2 end after the println! where they are last used, which is before the mutable reference r3 is created.

Dangling references

In Rust, the compiler guarantees that a reference will never be a dangling reference. If there is a reference to some data, the compiler will ensure that the data will not go out of scope before the reference to the data does.

fn dangle() -> &String { // dangle returns a reference to a String

    let s = String::from("hello"); // s is a new String

    &s // we return a reference to the String, s
} // Here, s goes out of scope and is dropped, so its memory goes away.
  // Danger!

CONCLUSION

Slice type

Slice let you reference a contiguous sequence of elements in a Collection.
A slice is a kind of reference, it does not have ownership.

let s = String::from("hello");
let len = s.len();

// same
let slice = &s[0..2];
let slice = &s[..2];


// same
let slice = &s[2..len];
let slice = &s[2..];

//same
let slice = &s[0..len];
let slice = &s[..]